package com.git.hui.yinshua.biz.common.oss.impl;

import com.alibaba.fastjson.JSONObject;
import com.git.hui.yinshua.api.model.ReqInfoContext;
import com.git.hui.yinshua.biz.common.ImageProperties;
import com.git.hui.yinshua.biz.common.oss.ImageUploader;
import com.git.hui.yinshua.biz.rest.models.img.ImgTokenDto;
import com.git.hui.yinshua.biz.util.Md5Util;
import com.git.hui.yinshua.common.DateUtil;
import com.qiniu.http.Response;
import com.qiniu.storage.Configuration;
import com.qiniu.storage.UploadManager;
import com.qiniu.storage.model.DefaultPutRet;
import com.qiniu.util.Auth;
import com.qiniu.util.StringMap;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
import org.springframework.stereotype.Component;
import org.springframework.util.StreamUtils;

import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.URL;
import java.net.URLEncoder;

import static com.git.hui.yinshua.api.model.constants.GlobalConstants.ImgApi.EXPIRE_IN_SECONDS;

/**
 * @author YiHui
 * @date 2024/7/1
 */
@Slf4j
@ConditionalOnExpression(value = "#{'qiniu'.equals(environment.getProperty('image.oss.type'))}")
@Component
public class QiniuOssWrapper implements ImageUploader {
    private ImageProperties imageProperties;
    private static Auth auth;

    public QiniuOssWrapper(ImageProperties imageProperties) {
        auth = Auth.create(imageProperties.getOss().getAk(), imageProperties.getOss().getSk());
        this.imageProperties = imageProperties;
    }

    /**
     * 返回给前端用于上传的token
     *
     * @return
     */
    public ImgTokenDto getUploadToken() {
        String scope = imageProperties.getOss().getBucket() + ":" + imageProperties.getOss().getPrefix();

        Long deadLine = System.currentTimeMillis() / 1000 + EXPIRE_IN_SECONDS;
        String returnBody = "{\n" +
                "      \"size\": $(fsize),\n" +
                "      \"w\": $(imageInfo.width),\n" +
                "      \"h\": $(imageInfo.height),\n" +
                "      \"hash\": $(etag)\n" +
                "}";
        StringMap obj = new StringMap();
        obj.put("scope", scope);
        obj.put("deadline", deadLine);
        obj.put("returnBody", returnBody);

        String token = auth.uploadToken(imageProperties.getOss().getBucket(), null, EXPIRE_IN_SECONDS, obj);
        ImgTokenDto res = new ImgTokenDto();
        res.setToken(token);
        res.setExpireAt(deadLine);
        res.setHost(imageProperties.getOss().getHost());
        return res;
    }

    @Override
    public String buildAccessSign(String url) {
        if (!url.startsWith(imageProperties.getOss().getHost())) {
            return url;
        }

        if (url.indexOf("?") > 0) {
            // 已经签过名了，直接返回
            return url;
        }

        try {
            URL u = new URL(url);
            String domain = u.getHost();
            String path = u.getPath();
            return buildSignUrl(domain, path);
        } catch (Exception e) {
            log.error("签名异常: {}", url, e);
            return url;
        }
    }

    private static String buildSignUrl(String domain, String fileName) throws UnsupportedEncodingException {
        String domainOfBucket = domain.startsWith("http") ? domain : "http://" + domain;
        if (!domainOfBucket.endsWith("/")) {
            domainOfBucket = domainOfBucket + "/";
        }
        if (fileName.startsWith("/")) {
            fileName = fileName.substring(1);
        }
        String encodedFileName = URLEncoder.encode(fileName, "utf-8").replace("+", "%20").replace("%2F", "/");
        String publicUrl = String.format("%s%s", domainOfBucket, encodedFileName);
        return auth.privateDownloadUrl(publicUrl, EXPIRE_IN_SECONDS);
    }

    /**
     * 文件上传
     *
     * @param input
     * @param fileType
     * @return
     */
    @Override
    public String upload(InputStream input, String fileType) {
        //构造一个带指定 Region 对象的配置类
        Configuration cfg = new Configuration();
        cfg.resumableUploadAPIVersion = Configuration.ResumableUploadAPIVersion.V2;// 指定分片上传版本
        //...其他参数参考类注释
        UploadManager uploadManager = new UploadManager(cfg);
        //...生成上传凭证，然后准备上传
        String bucket = imageProperties.getOss().getBucket();
        //默认不指定key的情况下，以文件内容的hash值作为文件名
        try {
            byte[] bytes = StreamUtils.copyToByteArray(input);
            String upFileName = this.genImgFilePath(bytes, fileType);
            String upToken = auth.uploadToken(bucket);
            Response response = uploadManager.put(bytes, upFileName, upToken);
            //解析上传成功的结果
            DefaultPutRet putRet = JSONObject.parseObject(response.bodyString(), DefaultPutRet.class);
            System.out.println(putRet.key);
            System.out.println(putRet.hash);
            return imageProperties.getOss().getHost() + upFileName;
        } catch (Exception ex) {
            //ignore
            log.error("upload image error response!", ex);
            return null;
        }
    }

    /**
     * 获取文件上传的路径
     *
     * @return
     */
    private String genImgFilePath(byte[] bytes, String fileType) {
        String path;
        Integer uid = ReqInfoContext.getReqInfo().getUserId();
        if (uid == null) {
            uid = 0;
            // 用户的图片，按月进行归档
            path = DateUtil.getToday();
        } else {
            // 非用户的图片，按天进行归档
            path = DateUtil.getThisMonth();
        }
        String fileName = Md5Util.encode(bytes);
        return "ys/" + uid + "/" + path + "/" + fileName + "." + fileType;
    }

    @Override
    public boolean uploadIgnore(String fileUrl) {
        return fileUrl.startsWith(imageProperties.getOss().getHost());
    }
}
